﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;

namespace Behavioral.Tests
{
    [TestFixture]
    public class ExceptionTestsWithoutReturnValue
    {
        #region InitializerExceptionsAreNotThrownWithoutActions

        [Test]
        public void InitializerExceptionsAreNotThrownWithoutActions()
        {
            var testClass = new TestWithThrowingInitializer();
            try
            {
                testClass.SetUpThrowingInitializer();
            }
            catch (Exception ex)
            {
                Assert.Fail("No exception expected, but {0} caught", ex.GetType().Name);
            }
            Assert.Pass();
        }

        class TestWithThrowingInitializer : UnitTest<int>
        {
            public void SetUpThrowingInitializer()
            {
                GivenThat<AnInitializerThatThrows>();
            }
        }

        class AnInitializerThatThrows : IInitializer<int>
        {
            public void SetUp(ref int target)
            {
                throw new NotImplementedException();
            }
        }

        #endregion

        #region InitializerExceptionsAreThrownWithActions

        [Test]
        public void InitializerExceptionsAreThrownWithActions()
        {
            var testClass = new TestWithThrowingInitializerAndNonThrowingAction();

            try
            {
                testClass.SetUpThrowingInitializer();
            }
            catch (Exception ex)
            {
                Assert.IsInstanceOf<NotImplementedException>(ex);
                Assert.AreEqual("SetUp", ex.TargetSite.Name);
                return;
            }
            Assert.Fail("Exception not thrown at all");
        }

        class TestWithThrowingInitializerAndNonThrowingAction : UnitTest<int>
        {
            public void SetUpThrowingInitializer()
            {
                GivenThat<AnInitializerThatThrows>()
                .When<NoOpActionIsExecuted>();
            }
        }

        class NoOpActionIsExecuted : IAction<int>
        {
            public void Execute(int target)
            {
                throw new NotImplementedException();
            }
        }

        #endregion

        #region ActionExceptionsAreNotThrownWithoutExpectations

        [Test]
        public void ActionExceptionsAreNotThrownWithoutExpectations()
        {
            var testClass = new TestWithThrowingAction();
            try
            {
                testClass.SetUpThrowingAction();
            }
            catch (Exception ex)
            {
                Assert.Fail("No exception expected, but {0} caught", ex.GetType().Name);
            }
            Assert.Pass();
        }

        class TestWithThrowingAction : UnitTest<int>
        {
            public void SetUpThrowingAction()
            {
                GivenThat<AnInitializerDoesNotThrow>()
                .When<AThrowingActionIsAdded>();
            }
        }

        public class AnInitializerDoesNotThrow : IInitializer<int>
        {
            public void SetUp(ref int target)
            {
                
            }
        }

        public class AThrowingActionIsAdded : IAction<int>
        {
            public void Execute(int target)
            {
                throw new ArithmeticException();
            }
        }

        #endregion

        #region UnexpectedActionExceptionsShouldBeThrownFromSource

        [Test]
        public void UnexpectedActionExceptionsShouldBeThrownFromSource()
        {
            var testClass = new TestWithUnexpectedThrowingAction();

            try
            {
                testClass.SetUpUnexpectedThrowingAction();
            }
            catch (Exception ex)
            {
                Assert.IsInstanceOf<ArithmeticException>(ex);
                Assert.AreEqual("Execute", ex.TargetSite.Name);
                return;
            }
            Assert.Fail("Exception not thrown at all");
        }

        public class TestWithUnexpectedThrowingAction : UnitTest<int>
        {
            public void SetUpUnexpectedThrowingAction()
            {
                GivenThat<AnInitializerDoesNotThrow>()
                .When<AThrowingActionIsAdded>()
                .Then<AnAssertionIsExpected>();
            }
        }

        public class AnAssertionIsExpected : IAssertion<int>
        {
            public void Verify(int target)
            {
                
            }
        }

        #endregion

        #region ExpectedExceptionsShouldNotBeThrown

        [Test]
        public  void ExpectedExceptionsShouldNotBeThrown()
        {
            var testClass = new TestWithExpectedException();
            try
            {
                testClass.SetUpTestWithExpectedException();
            }
            catch (Exception ex)
            {
                Assert.Fail("No exception expected, but {0} caught", ex.GetType().Name);
            }
            Assert.Pass();
        }

        public class TestWithExpectedException : UnitTest<int>
        {
            public void SetUpTestWithExpectedException()
            {
                GivenThat<AnInitializerDoesNotThrow>()
                .When<AThrowingActionIsAdded>()
                .ThenThrow<ArithmeticException>();
            }
        }

        #endregion

        #region ExceptionsDifferingFromExpectationsShouldBeThrown

        [Test]
        public void ExceptionsDifferingFromExpectationsShouldBeThrown()
        {
            var testClass = new TestWithMismatchingExpectedException();
            try
            {
                testClass.SetUpTestWithMismatchingExpectedException();
            }
            catch (Exception ex)
            {
                Assert.IsInstanceOf<ArithmeticException>(ex);
                Assert.AreEqual("Execute", ex.TargetSite.Name);
                return;
            }
            Assert.Fail("Exception not thrown at all");
        }

        public class TestWithMismatchingExpectedException : UnitTest<int>
        {
            public void SetUpTestWithMismatchingExpectedException()
            {
                GivenThat<AnInitializerDoesNotThrow>()
                .When<AThrowingActionIsAdded>()
                .ThenThrow<NullReferenceException>();
            }
        }

        #endregion

        #region SupertypesOfThrownExceptionsShouldNotBeThrown

        [Test]
        public void SupertypesOfThrownExceptionsShouldNotBeThrown()
        {
            var testClass = new TestWithExpectedSupertypeOfExpectedException();
            try
            {
                testClass.SetUpTestWithSupertypeOfExpectedException();
            }
            catch (Exception ex)
            {
                Assert.Fail("No exception expected, but {0} caught", ex.GetType().Name);
            }
            Assert.Pass();
        }

        public class TestWithExpectedSupertypeOfExpectedException : UnitTest<int>
        {
            public void SetUpTestWithSupertypeOfExpectedException()
            {
                GivenThat<AnInitializerDoesNotThrow>()
                .When<AThrowingActionIsAdded>()
                .ThenThrow<Exception>();
            }
        }

        #endregion

        #region SubtypesOfThrownExceptionsShouldBeThrown

        [Test]
        public void SubtypesOfThrownExceptionsShouldBeThrown()
        {
            var testClass = new TestWithExpectedSubeOfExpectedException();
            try
            {
                testClass.SetUpTestWithExpectedSubeOfExpectedException();
            }
            catch (Exception ex)
            {
                Assert.IsInstanceOf<ArithmeticException>(ex);
                Assert.AreEqual("Execute", ex.TargetSite.Name);
                return;
            }
            Assert.Fail("Exception not thrown at all");
        }

        public class TestWithExpectedSubeOfExpectedException : UnitTest<int>
        {
            public void SetUpTestWithExpectedSubeOfExpectedException()
            {
                GivenThat<AnInitializerDoesNotThrow>()
                .When<AThrowingActionIsAdded>()
                .ThenThrow<OverflowException>();
            }
        }

        #endregion

        #region ExpectingAnExceptionThatIsNotThrownShouldThrow

        [Test]
        public void ExpectingAnExceptionThatIsNotThrownShouldThrow()
        {
            var testClass = new TestThatExpectsButDoesNotThrow();
            try
            {
                testClass.SetUpTestWithExpectedNonThrowingAction();
            }
            catch (Exception)
            {
                Assert.Pass();
                return;
            }
            Assert.Fail("Exception not thrown at all");
        }

        class TestThatExpectsButDoesNotThrow : UnitTest<int>
        {
            public void SetUpTestWithExpectedNonThrowingAction()
            {
                GivenThat<AnInitializerDoesNotThrow>()
                .When<NonThrowingActionIsExecuted>()
                .ThenThrow<Exception>();
            }
        }

        class NonThrowingActionIsExecuted : IAction<int, int>
        {
            public int Execute(int target)
            {
                return 0;
            }
        }

        #endregion
    }

    [TestFixture]
    public class ExceptionTestsWithReturnValue
    {
        #region InitializerExceptionsAreNotThrownWithoutActions

        [Test]
        public void InitializerExceptionsAreNotThrownWithoutActions()
        {
            var testClass = new TestWithThrowingInitializer();
            try
            {
                testClass.SetUpThrowingInitializer();
            }
            catch (Exception ex)
            {
                Assert.Fail("No exception expected, but {0} caught", ex.GetType().Name);
            }
            Assert.Pass();
        }

        class TestWithThrowingInitializer : UnitTest<int, int>
        {
            public void SetUpThrowingInitializer()
            {
                GivenThat<AnInitializerThatThrows>();
            }
        }

        class AnInitializerThatThrows : IInitializer<int>
        {
            public void SetUp(ref int target)
            {
                throw new NotImplementedException();
            }
        }

        #endregion

        #region InitializerExceptionsAreThrownWithActions

        [Test]
        public void InitializerExceptionsAreThrownWithActions()
        {
            var testClass = new TestWithThrowingInitializerAndNonThrowingAction();

            try
            {
                testClass.SetUpThrowingInitializer();
            }
            catch (Exception ex)
            {
                Assert.IsInstanceOf<NotImplementedException>(ex);
                Assert.AreEqual("SetUp", ex.TargetSite.Name);
                return;
            }
            Assert.Fail("Exception not thrown at all");
        }

        class TestWithThrowingInitializerAndNonThrowingAction : UnitTest<int, int>
        {
            public void SetUpThrowingInitializer()
            {
                GivenThat<AnInitializerThatThrows>()
                .When<NoOpActionIsExecuted>();
            }
        }

        class NoOpActionIsExecuted : IAction<int>
        {
            public void Execute(int target)
            {
                throw new NotImplementedException();
            }
        }

        class NonThrowingActionIsExecuted : IAction<int>
        {
            public void Execute(int target)
            {
                
            }
        }

        #endregion

        #region ActionExceptionsAreNotThrownWithoutExpectations

        [Test]
        public void ActionExceptionsAreNotThrownWithoutExpectations()
        {
            var testClass = new TestWithThrowingAction();
            try
            {
                testClass.SetUpThrowingAction();
            }
            catch (Exception ex)
            {
                Assert.Fail("No exception expected, but {0} caught", ex.GetType().Name);
            }
            Assert.Pass();
        }

        class TestWithThrowingAction : UnitTest<int, int>
        {
            public void SetUpThrowingAction()
            {
                GivenThat<AnInitializerDoesNotThrow>()
                .When<AThrowingActionIsAdded>();
            }
        }

        public class AnInitializerDoesNotThrow : IInitializer<int>
        {
            public void SetUp(ref int target)
            {

            }
        }

        public class AThrowingActionIsAdded : IAction<int, int>
        {
            public int Execute(int target)
            {
                throw new ArithmeticException();
            }
        }

        #endregion

        #region UnexpectedActionExceptionsShouldBeThrownFromSource

        [Test]
        public void UnexpectedActionExceptionsShouldBeThrownFromSource()
        {
            var testClass = new TestWithUnexpectedThrowingAction();

            try
            {
                testClass.SetUpUnexpectedThrowingAction();
            }
            catch (Exception ex)
            {
                Assert.IsInstanceOf<ArithmeticException>(ex);
                Assert.AreEqual("Execute", ex.TargetSite.Name);
                return;
            }
            Assert.Fail("Exception not thrown at all");
        }

        public class TestWithUnexpectedThrowingAction : UnitTest<int, int>
        {
            public void SetUpUnexpectedThrowingAction()
            {
                GivenThat<AnInitializerDoesNotThrow>()
                .When<AThrowingActionIsAdded>()
                .Then<AnAssertionIsExpected>();
            }
        }

        public class AnAssertionIsExpected : IAssertion<int, int>
        {
            public void Verify(int target, int returnValue)
            {

            }
        }

        #endregion

        #region ExpectedExceptionsShouldNotBeThrown

        [Test]
        public void ExpectedExceptionsShouldNotBeThrown()
        {
            var testClass = new TestWithExpectedException();
            try
            {
                testClass.SetUpTestWithExpectedException();
            }
            catch (Exception ex)
            {
                Assert.Fail("No exception expected, but {0} caught", ex.GetType().Name);
            }
            Assert.Pass();
        }

        public class TestWithExpectedException : UnitTest<int, int>
        {
            public void SetUpTestWithExpectedException()
            {
                GivenThat<AnInitializerDoesNotThrow>()
                .When<AThrowingActionIsAdded>()
                .ThenThrow<ArithmeticException>();
            }
        }

        #endregion

        #region ExceptionsDifferingFromExpectationsShouldBeThrown

        [Test]
        public void ExceptionsDifferingFromExpectationsShouldBeThrown()
        {
            var testClass = new TestWithMismatchingExpectedException();
            try
            {
                testClass.SetUpTestWithMismatchingExpectedException();
            }
            catch (Exception ex)
            {
                Assert.IsInstanceOf<ArithmeticException>(ex);
                Assert.AreEqual("Execute", ex.TargetSite.Name);
                return;
            }
            Assert.Fail("Exception not thrown at all");
        }

        public class TestWithMismatchingExpectedException : UnitTest<int, int>
        {
            public void SetUpTestWithMismatchingExpectedException()
            {
                GivenThat<AnInitializerDoesNotThrow>()
                .When<AThrowingActionIsAdded>()
                .ThenThrow<NullReferenceException>();
            }
        }

        #endregion

        #region SupertypesOfThrownExceptionsShouldNotBeThrown

        [Test]
        public void SupertypesOfThrownExceptionsShouldNotBeThrown()
        {
            var testClass = new TestWithExpectedSupertypeOfExpectedException();
            try
            {
                testClass.SetUpTestWithSupertypeOfExpectedException();
            }
            catch (Exception ex)
            {
                Assert.Fail("No exception expected, but {0} caught", ex.GetType().Name);
            }
            Assert.Pass();
        }

        public class TestWithExpectedSupertypeOfExpectedException : UnitTest<int, int>
        {
            public void SetUpTestWithSupertypeOfExpectedException()
            {
                GivenThat<AnInitializerDoesNotThrow>()
                .When<AThrowingActionIsAdded>()
                .ThenThrow<Exception>();
            }
        }

        #endregion

        #region SubtypesOfThrownExceptionsShouldBeThrown

        [Test]
        public void SubtypesOfThrownExceptionsShouldBeThrown()
        {
            var testClass = new TestWithExpectedSubeOfExpectedException();
            try
            {
                testClass.SetUpTestWithExpectedSubeOfExpectedException();
            }
            catch (Exception ex)
            {
                Assert.IsInstanceOf<ArithmeticException>(ex);
                Assert.AreEqual("Execute", ex.TargetSite.Name);
                return;
            }
            Assert.Fail("Exception not thrown at all");
        }

        public class TestWithExpectedSubeOfExpectedException : UnitTest<int, int>
        {
            public void SetUpTestWithExpectedSubeOfExpectedException()
            {
                GivenThat<AnInitializerDoesNotThrow>()
                .When<AThrowingActionIsAdded>()
                .ThenThrow<OverflowException>();
            }
        }

        #endregion

        #region ExpectingAnExceptionThatIsNotThrownShouldThrow

        [Test]
        public void ExpectingAnExceptionThatIsNotThrownShouldThrow()
        {
            var testClass = new TestThatExpectsButDoesNotThrow();
            try
            {
                testClass.SetUpTestWithExpectedNonThrowingAction();
            }
            catch (Exception)
            {
                Assert.Pass();
                return;
            }
            Assert.Fail("Exception not thrown at all");
        }

        class TestThatExpectsButDoesNotThrow : UnitTest<int, int>
        {
            public void SetUpTestWithExpectedNonThrowingAction()
            {
                GivenThat<AnInitializerDoesNotThrow>()
                .When<NonThrowingActionIsExecuted>()
                .ThenThrow<Exception>();
            }
        }

        #endregion
    }
}
